package middleware

import (
	"encoding/json"
	"github.com/gin-gonic/gin"
	"github.com/jhoncy/schelper/lib"
	"github.com/jhoncy/schelper/model"
	"net/http"
	"strings"
)

type policy struct {
	Resource []string
	Action 	 []string
}

func RoleAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		username, role := lib.JwtParse(c)
		if username == "" || role == "" {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error": "user not logged in or time out",
			})
		}
		var policies []model.CoreRolePolicy
		model.DB().Where("role_name = ?", role).Select("policy").Find(&policies)
		if policies == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error": "permission denied",
			})
		}
		path := strings.Split(strings.ReplaceAll(strings.TrimPrefix(c.FullPath(),"/"),":",""), "/")
		method := c.Request.Method
		if checkResource(policies,path) && checkAction(policies, path, method){
			c.Next()
			return
		}
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
			"error": "permission denied",
		})
	}
}

//校验资源
func checkResource(policies []model.CoreRolePolicy, path []string) bool {
	for _, mod := range policies {
		var policy []policy
		err := json.Unmarshal([]byte(mod.Policy), &policy)
		if err != nil {
			return false
		}
		for _, exp := range policy {
			for _, resource := range exp.Resource {
				r := strings.Split(resource, ":")
				//超出匹配范围
				if len(r) > len(path)+1 {
					continue
				}
				//小于等于匹配范围
				if len(r) <= len(path)+1 {
					for i := 0; i < len(r); i++ {
						if r[i] == "*" {
							if i == len(r)-1 {
								return true
							}
							continue
						}
						if r[i] == path[i] {
							if i == len(r)-1 {
								return true
							}
						} else {
							break
						}
					}
				}
			}
		}
	}
	return false
}

//校验行为
func checkAction(policies []model.CoreRolePolicy, path []string, method string) bool {
	for _, mod := range policies {
		var policy []policy
		err := json.Unmarshal([]byte(mod.Policy), &policy)
		if err != nil {
			return false
		}
		for _, exp := range policy {
			for _, action := range exp.Action {
				a := strings.Split(action, ":")
				if a[len(a)-1] != "*" && a[len(a)-1] != strings.ToLower(method) {
					return false
				}
				a = a[0:len(a)-1]
				//超出匹配范围
				if len(a) > len(path)+1 {
					continue
				}
				//小于等于匹配范围
				if len(a) <= len(path)+1 {
					for i := 0; i < len(a); i++ {
						if a[i] == "*" {
							if i == len(a)-1 {
								return true
							}
							continue
						}
						if a[i] == path[i] {
							if i == len(a)-1 {
								return true
							}
							continue
						}
					}
				}
			}
		}
	}
	return false
}